﻿#include <Windows.h>
#include <algorithm>

#include "Tag.h"

#include "../Utils/Logger.h"
#include "../Utils/Utils.h"

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

Int64Tag::Int64Tag(int64_t value) {
	static uintptr_t** Int64TagVtable = 0x0;
	if (Int64TagVtable == 0x0) [[unlikely]] {
		uintptr_t sigOffset = Utils::Utils::getBase() + 26695763;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		Int64TagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(Int64Tag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)Int64TagVtable;
	this->value = value;
}

void Int64Tag::read(std::u8string_view str) {  // throws MojangsonParseException
#ifdef HELIUM_DISABLE_EXCEPTIONS
	value = fast_io::u8to<int64_t>(str);
#else
	try {
		value = fast_io::u8to<int64_t>(str);
	} catch (const fast_io::error&) {
	}
#endif
}

IntTag::IntTag(int value) {
	static uintptr_t** IntTagVtable = 0x0;
	if (IntTagVtable == 0x0) [[unlikely]] {
		uintptr_t sigOffset = Utils::Utils::getBase() + 24236405;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		IntTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(IntTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)IntTagVtable;
	this->value = value;
}

void IntTag::read(std::u8string_view string) {  // throws MojangsonParseException
#ifdef HELIUM_DISABLE_EXCEPTIONS

	value = fast_io::u8to<int32_t>(string);

#else
	try {
		value = fast_io::u8to<int32_t>(string);
	} catch (const fast_io::error&) {
	}
#endif
}

StringTag::StringTag(std::u8string_view value) {
	static uintptr_t** StringTagVtable = 0x0;
	if (StringTagVtable == 0x0) [[unlikely]] {
		uintptr_t sigOffset = Utils::Utils::getBase() + 24332789;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		StringTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(StringTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)StringTagVtable;
	this->value = value;
}

ShortTag::ShortTag(short value) {
	static uintptr_t** ShortTagVtable = 0x0;
	if (ShortTagVtable == 0x0) [[unlikely]] {
		uintptr_t sigOffset = Utils::Utils::getBase() + 24240678;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		ShortTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(ShortTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)ShortTagVtable;
	this->value = value;
}

void ShortTag::read(std::u8string_view string) {  // throws MojangsonParseException
#ifdef HELIUM_DISABLE_EXCEPTIONS

	value = fast_io::u8to<int16_t>(string);

#else
	try {
		value = fast_io::u8to<int16_t>(string);
	} catch (const fast_io::error&) {
	}
#endif
}

FloatTag::FloatTag(float value) {
	static uintptr_t** FloatTagVtable = 0x0;
	if (FloatTagVtable == 0x0) [[unlikely]] {
		uintptr_t sigOffset = Utils::Utils::getBase() + 26721755;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		FloatTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(FloatTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)FloatTagVtable;
	this->value = value;
}

void FloatTag::read(std::u8string string) {  // MojangsonParseException

	if (string.size() > 0) [[likely]] {
		std::ranges::for_each(string, [](char8_t& str) { str = fast_io::char_category::to_c_lower(str); });
		if (string.back() == MojangsonToken::FLOAT_SUFFIX.getSymbol())
			string.pop_back();
	}
	// try {
	//	value = fast_io::u8to<float>(string);
	// } catch (const fast_io::error&) {
	// }
#ifdef HELIUM_DISABLE_EXCEPTIONS
	value = std::stof(*(std::string*)&string);
#else
	try {
		value = std::stof(*(std::string*)&string);
	} catch (const std::exception&) {
	}
#endif
}

void DoubleTag::read(std::u8string string) {  // throws MojangsonParseException

	if (string.size() > 0) [[likely]] {
		std::ranges::for_each(string, [](char8_t& str) { str = fast_io::char_category::to_c_lower(str); });
		if (string.back() == MojangsonToken::DOUBLE_SUFFIX.getSymbol())
			string.pop_back();
	}

	// try {
	//	value = fast_io::u8to<double>(string);
	// } catch (const fast_io::error&) {
	// }
#ifdef HELIUM_DISABLE_EXCEPTIONS
	value = std::stod(*(std::string*)&string);
#else
	try {
		value = std::stod(*(std::string*)&string);
	} catch (const std::exception&) {
	}
#endif
}

ByteTag::ByteTag(char value) {
	static uintptr_t** ByteTagVtable = 0x0;
	if (ByteTagVtable == 0x0) {
		uintptr_t sigOffset = Utils::Utils::getBase() + 26693510;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		ByteTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(ByteTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)ByteTagVtable;
	this->value = value;
}

void ByteTag::read(std::u8string_view string) {  // throws MojangsonParseException
#ifdef HELIUM_DISABLE_EXCEPTIONS
	value = fast_io::u8to<int8_t>(string);
#else
	try {
		value = fast_io::u8to<int8_t>(string);
	} catch (const fast_io::error&) {
	}
#endif
}

ListTag::ListTag() {
	static uintptr_t** ListTagVtable = 0x0;
	if (ListTagVtable == 0x0) {
		uintptr_t sigOffset = Utils::Utils::getBase() + 24332651;
		int offset = *reinterpret_cast<int*>(sigOffset + 3);
		ListTagVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + /*length of instruction*/ 7);
	}
	fast_io::freestanding::my_memset(this, 0, sizeof(ListTag));  // Avoid overwriting vtable
	uintptr_t* _this = reinterpret_cast<uintptr_t*>(this);
	*_this = (uintptr_t)ListTagVtable;
}

void ListTag::add(std::unique_ptr<Tag> tag) {
	using ListTag__add_t = void(__fastcall*)(ListTag*, std::unique_ptr<Tag>);
	static ListTag__add_t func = reinterpret_cast<ListTag__add_t>(Utils::Utils::getBase() + 24238512);
	func(this, std::move(tag));
}

void CompoundTag::write(std::u8string& builder) {
	builder.push_back(MojangsonToken::COMPOUND_START.getSymbol());
	bool start = true;

	class CompoundTagVariant {
	private:
		uint8_t pad_0x0[0x20];  // 0x0000

	public:
		TextHolder keyName;  // 0x0020
		Tag tag;             // 0x0040
	};

	__int64* v10 = *(__int64**)(uintptr_t(this) + 0x8);
	__int64* v11 = (__int64*)*v10;
	__int64 i = 0;
	if ((__int64*)*v10 != v10) {
		do {
			if (start) {
				start = false;
			} else {
				builder.push_back(MojangsonToken::ELEMENT_SEPERATOR.getSymbol());
			}
			CompoundTagVariant* variant = reinterpret_cast<CompoundTagVariant*>(v11);

			if (variant) {
				builder += fast_io::u8concat(fast_io::mnp::os_c_str(variant->keyName.getText()), fast_io::mnp::chvw(MojangsonToken::ELEMENT_PAIR_SEPERATOR.getSymbol()));
				Handler::handleWrite(&variant->tag, builder);
			}

			__int64** v17 = (__int64**)v11[2];
			if (*((BYTE*)v17 + 0x19)) {
				for (i = v11[1]; !*(BYTE*)(i + 0x19); i = *(__int64*)(i + 8)) {
					if (v11 != *(__int64**)(i + 0x10))
						break;
					v11 = (__int64*)i;
				}
				v11 = (__int64*)i;
			} else {
				v11 = (__int64*)v11[2];
				for (__int64* j = *v17; !*((BYTE*)j + 25); j = (__int64*)*j)
					v11 = j;
			}

		} while (v11 != *(__int64**)(uintptr_t(this) + 0x8));
	}

	/*for (std::map<std::string, Tag>::iterator it = this->map.begin(); it != this->map.end(); it++) {
		if (start) {
			start = false;
		} else {
			builder << MojangsonToken::ELEMENT_SEPERATOR.getSymbol();
		}
		builder << it->first << MojangsonToken::ELEMENT_PAIR_SEPERATOR.getSymbol();
		yeet::handleWrite(&it->second, builder);
	}*/
	builder.push_back(MojangsonToken::COMPOUND_END.getSymbol());
}

CompoundTag::CompoundTag() {
	fast_io::freestanding::my_memset(this, 0, sizeof(CompoundTag));
	using constructor_t = void(__fastcall*)(CompoundTag*);
	static constructor_t func = reinterpret_cast<constructor_t>(Utils::Utils::getBase() + 26693712);
	func(this);
}

void CompoundTag::put(TextHolder_view& tag, std::unique_ptr<Tag> value) {
	using CompoundTag__putF = void(__fastcall*)(CompoundTag*, TextHolder_view&, std::unique_ptr<Tag>);
	static CompoundTag__putF func = reinterpret_cast<CompoundTag__putF>(Utils::Utils::getBase() + 26693712);
	func(this, tag, std::move(value));
}

void Handler::handleWrite(Tag* value, std::u8string& builder) {
	switch (static_cast<TagType>(value->getId())) {
	case TagType::BYTE:
		reinterpret_cast<ByteTag*>(value)->write(builder);
		break;
	case TagType::COMPOUND:
		reinterpret_cast<CompoundTag*>(value)->write(builder);
		break;
	case TagType::DOUBLE:
		reinterpret_cast<DoubleTag*>(value)->write(builder);
		break;
	case TagType::FLOAT:
		reinterpret_cast<FloatTag*>(value)->write(builder);
		break;
	case TagType::INT:
		reinterpret_cast<IntTag*>(value)->write(builder);
		break;
	case TagType::LIST:
		reinterpret_cast<ListTag*>(value)->write(builder);
		break;
	case TagType::LONG:
		reinterpret_cast<Int64Tag*>(value)->write(builder);
		break;
	case TagType::SHORT:
		reinterpret_cast<ShortTag*>(value)->write(builder);
		break;
	case TagType::STRING:
		reinterpret_cast<StringTag*>(value)->write(builder);
		break;
	case TagType::BYTE_ARRAY:
		Logger::LogC(LogGrade::error, u8"handleWrite: BYTE ARRAY CANNOT HANDLE");
		break;
	case TagType::INT_ARRAY:
		Logger::LogC(LogGrade::error, u8"handleWrite: INT ARRAY CANNOT HANDLE");
		break;
	default:
		break;
	}
}

std::unique_ptr<Tag> Mojangson::parseTag(std::u8string mojangson) {
	if (mojangson.front() == MojangsonToken::COMPOUND_START.getSymbol() && mojangson.back() != MojangsonToken::COMPOUND_END.getSymbol()) {
		mojangson.push_back(u8'}');
	}
	if (mojangson.front() == MojangsonToken::STRING_QUOTES.getSymbol() && mojangson.back() == MojangsonToken::STRING_QUOTES.getSymbol()) {
		std::unique_ptr<StringTag> val = std::make_unique<StringTag>(u8"");
		val->read(mojangson);
		return val;
	}
	if (mojangson.front() == MojangsonToken::ARRAY_START.getSymbol() && mojangson.back() == MojangsonToken::ARRAY_END.getSymbol()) {
		std::unique_ptr<ListTag> val = std::make_unique<ListTag>();
		val->read(mojangson);
		return val;
	}
	if (mojangson.front() == MojangsonToken::COMPOUND_START.getSymbol() && mojangson.back() == MojangsonToken::COMPOUND_END.getSymbol()) {
		std::unique_ptr<CompoundTag> val = std::make_unique<CompoundTag>();
		val->read(mojangson);
		return val;
	}
	if (mojangson.back() == MojangsonToken::BYTE_SUFFIX.getSymbol()) {
		std::unique_ptr<ByteTag> val = std::make_unique<ByteTag>(0i8);
		val->read(mojangson);
		return val;
	}
	if (mojangson.back() == MojangsonToken::DOUBLE_SUFFIX.getSymbol() /*|| mojangson.contains(".")*/) {
		std::unique_ptr<DoubleTag> val = std::make_unique<DoubleTag>(0.);
		val->read(mojangson);
		return val;
	}
	if (mojangson.back() == MojangsonToken::LONG_SUFFIX.getSymbol()) {
		std::unique_ptr<Int64Tag> val = std::make_unique<Int64Tag>(0);
		val->read(mojangson);
		return val;
	}
	if (mojangson.back() == MojangsonToken::FLOAT_SUFFIX.getSymbol()) {
		std::unique_ptr<FloatTag> val = std::make_unique<FloatTag>(0.f);
		val->read(mojangson);
		return val;
	}
	if (mojangson.back() == MojangsonToken::SHORT_SUFFIX.getSymbol()) {
		std::unique_ptr<ShortTag> val = std::make_unique<ShortTag>(0i16);
		val->read(mojangson);
		return val;
	}
	std::unique_ptr<IntTag> val = std::make_unique<IntTag>(0);
	val->read(mojangson);
	return val;
}